﻿#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PHMEE.CommonUtils;
using PHMEE.Localization;

namespace PHMEE.EnergyEfficiency;

public class VesselEEInfoAppService : PHMEEAppService, IVesselEEInfoAppService
{
    private readonly ILogAbstractRepository _logAbstractRepository;
    private readonly LogAbstractDomainService _logAbstractDomainService;
    private readonly VesselEeConfigDomainService _vesselEeConfigDomainService;
    private readonly IPHMEETimeProvider _phmeeTimeProvider;

    public VesselEEInfoAppService(
        IPHMEETimeProvider phmeeTimeProvider,
        ILogAbstractRepository logAbstractRepository,
        LogAbstractDomainService logAbstractDomainService,
        VesselEeConfigDomainService vesselEeConfigDomainService)
    {
        LocalizationResource = typeof(EnergyEfficiencyResource);
        
        _phmeeTimeProvider = phmeeTimeProvider;
        _logAbstractRepository = logAbstractRepository;
        _logAbstractDomainService = logAbstractDomainService;
        _vesselEeConfigDomainService = vesselEeConfigDomainService;
    }

    // /// <inheritdoc />
    // public async Task<VesselEEInfoDto> CurrentEEInfoAsync(Guid vesselId)
    // {
    //     DateTime now = _phmeeTimeProvider.GetUtcNow();
    //
    //     var vessel = await _vesselNullableRepository.GetLatestRecordOrNewObjectFor6HoursAsync(vesselId, vesselId, now);
    //
    //     var vesselDto = ObjectMapper.Map<VesselNullable, VesselEEInfoDto>(vessel);
    //
    //     if (vesselDto.Longitude.HasValue && vesselDto.Latitude.HasValue)
    //     {
    //         string latitudeDM = ((decimal)vesselDto.Latitude.Value).LatitudeToDMS();
    //         string longitudeDM = ((decimal)vesselDto.Longitude.Value).LongitudeToDMS();
    //
    //         vesselDto = vesselDto with
    //         {
    //             LatitudeDM = latitudeDM,
    //             LongitudeDM = longitudeDM
    //         };
    //     }
    //
    //     return vesselDto;
    // }

    /// <inheritdoc />
    public async Task<EEOIDto> AnnualEEOI(Guid vesselId, int? year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        
        // 默认显示本年度 EEOI
        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        var vesselE2Config = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselE2Config == null)
        {
            return new EEOIDto(null);
        }

        var logAbstracts =
            await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        var eeoi = LogAbstractFormulas.EEOINullable(logAbstracts, vesselE2Config.FuelTypes, vesselE2Config.VesselType);

        return new EEOIDto(eeoi);
    }

    /// <inheritdoc />
    public async Task<EEOIDto> CurrentVoyageEEOI(Guid vesselId)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();

        var vesselE2Config = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselE2Config == null)
        {
            return new EEOIDto(null);
        }

        // 获取当前航次的所有 log abstracts
        var currentVoyageLogAbstracts = await _logAbstractDomainService.GetCurrentVoyageLogAbstracts(vesselId, utcNow);

        if (currentVoyageLogAbstracts.Count == 0)
        {
            // 当前并不在任何航次内
            return new EEOIDto(null);
        }

        var eeoi = LogAbstractFormulas.EEOINullable(currentVoyageLogAbstracts, vesselE2Config.FuelTypes,
            vesselE2Config.VesselType);

        return new EEOIDto(eeoi);
    }

    /// <inheritdoc />
    public Task<IReadOnlyList<EEOICIIComparisonTimeSeriesDatum>> EEOICompareDataAsync(Guid vesselId, DateTime startDate,
        DateTime endDate)
    {
        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDateRangeQuery(startDate, endDate);

        return EEOICompareDataByDateRangeAsync(vesselId, startDateTime, endDateTime);
    }

    /// <inheritdoc />
    public Task<IReadOnlyList<EEOICIIComparisonTimeSeriesDatum>> EEOICompareDataByYearAsync(Guid vesselId, int year)
    {
        var utcNow = _phmeeTimeProvider.GetUtcNow();
        
        var (_, startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForYearQuery(year, utcNow);

        return EEOICompareDataByDateRangeAsync(vesselId, startDateTime, endDateTime);
    }

    /// <summary>
    /// 按照日期段获取 EEOI 折线图的数据
    /// </summary>
    /// <param name="vesselId"></param>
    /// <param name="startDate"></param>
    /// <param name="endDate"></param>
    /// <returns></returns>
    private async Task<IReadOnlyList<EEOICIIComparisonTimeSeriesDatum>> EEOICompareDataByDateRangeAsync(Guid vesselId,
        DateTime startDate, DateTime endDate)
    {
        var vesselE2Config = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselE2Config == null)
        {
            return new List<EEOICIIComparisonTimeSeriesDatum>();
        }

        var logAbstracts = await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDate, endDate);
        var groupedLogAbstracts = logAbstracts.GroupBy(la => QueryDateTimeUtils.GetOpenCloseUtcDate(la.Ts)).ToDictionary(g => g.Key, g => g.ToList());

        var result = new List<EEOICIIComparisonTimeSeriesDatum>();

        // 我们只取 EndDate 0 点的数据，而前面 groupedLogAbstracts 时已经通过 CBMDateTimeHelper.GetOpenCloseUtcDate 方法将 0 点的数据算到前一天了
        // 所以这里 date < endDate 即可
        for (var date = startDate; date < endDate; date = date.AddDays(1))
        {
            if (groupedLogAbstracts.TryGetValue(date, out var logAbstractsForDate))
            {
                var eeoi = LogAbstractFormulas.EEOINullable(logAbstractsForDate, vesselE2Config.FuelTypes, vesselE2Config.VesselType);
                result.Add(new EEOICIIComparisonTimeSeriesDatum(date, eeoi, false));
            }
            else
            {
                // IsNoData 置为 true，说明是没有数据导致结果为 null
                result.Add(new EEOICIIComparisonTimeSeriesDatum(date, null, true));
            }
        }

        return result;
    }

    /// <inheritdoc />
    public async Task<DateRangeEEDataDto> DateRangeEEData(Guid vesselId,
        DateTime startDate, DateTime endDate)
    {
        var vesselE2Config = await _vesselEeConfigDomainService.GetOrDefaultByVesselIdAsync(vesselId);
        if (vesselE2Config == null)
        {
            return new DateRangeEEDataDto();
        }

        var (startDateTime, endDateTime) = QueryDateTimeUtils.ProcessForDateRangeQuery(startDate, endDate);

        var logAbstracts = await _logAbstractRepository.GetListOpenClosedAsync(vesselId, startDateTime, endDateTime);

        var result = new DateRangeEEDataDto
        {
            FuelConsumptionPerNauticalMile = LogAbstractFormulas.CSNullable(logAbstracts, vesselE2Config.FuelTypes),
            FuelConsumptionPerTransportWork = LogAbstractFormulas.CPNullable(logAbstracts, vesselE2Config.FuelTypes, vesselE2Config.VesselType),
            Co2EmissionsPerNauticalMile = LogAbstractFormulas.ESNullable(logAbstracts, vesselE2Config.FuelTypes),
            Co2EmissionsPerTransportUnit = LogAbstractFormulas.ECNullable(logAbstracts, vesselE2Config.FuelTypes, vesselE2Config.VesselType)
        };

        return result;
    }
}